package cn.zysheep;

import cn.zysheep.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * <p>
 *  Spring表达式语言全称为"Spring Expression Language"，缩写为"SpEL"，类似于Struts2x中使用的OGNL表达式语言，能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等，
 *  并且能与Spring功能完美整合，如能用来配置Bean定义。
 * </p>
 * <p>
 *  Spel能干什么?
 *  <ul>
 *    <li> 基本表达式： 字面量表达式、关系，逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式；
 *    <li> 类相关表达式： 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用；
 *    <li> 集合相关表达式： 内联List、内联数组、集合，字典访问、列表，字典，数组修改、集合投影、集合选择；不支持多维内联数组初始化；不支持内联字典定义；
 *    <li> 其他表达式：模板表达式。
 *  <ul/>
 * <p/>
 *
 *  SpEL表达式中的关键字是不区分大小写的。
 *
 * @author : lyw
 * @since : 2023/11/23 16:19
 */
@SpringBootTest(classes = SpelApplication.class)
public class SpelTest01 {

    @Autowired
    BeanFactory beanFactory;

    /**
     * 基本表达式 : 字面量表达式
     */
    @Test
    public void test01() {
        // 1）创建解析器：SpEL使用ExpressionParser接口表示解析器，提供SpelExpressionParser默认实现；
        ExpressionParser parser = new SpelExpressionParser();
        // 2）解析表达式：使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。
        Expression expression = parser.parseExpression("1+2");
        // 3）求值：通过Expression接口的getValue方法根据上下文获得表达式值。
        System.out.println(expression.getValue());



        String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);
        int int1 = parser.parseExpression("1").getValue(Integer.class);
        long long1 = parser.parseExpression("-1L").getValue(long.class);
        float float1 = parser.parseExpression("1.1").getValue(Float.class);
        double double1 = parser.parseExpression("1.1E+2").getValue(double.class);
        int hex1 = parser.parseExpression("0xa").getValue(Integer.class);
        long hex2 = parser.parseExpression("0xaL").getValue(long.class);
        boolean true1 = parser.parseExpression("true").getValue(boolean.class);
        boolean false1 = parser.parseExpression("false").getValue(boolean.class);
        Object null1 = parser.parseExpression("null").getValue(Object.class);

        System.out.println("str1=" + str1);
        System.out.println("int1=" + int1);
        System.out.println("long1=" + long1);
        System.out.println("float1=" + float1);
        System.out.println("double1=" + double1);
        System.out.println("hex1=" + hex1);
        System.out.println("hex2=" + hex2);
        System.out.println("true1=" + true1);
        System.out.println("false1=" + false1);
        System.out.println("null1=" + null1);
    }

    /**
     * 基本表达式 : 字面量表达式
     */
    @Test
    public void test02() {

        // 1）创建解析器：SpEL使用ExpressionParser接口表示解析器，提供SpelExpressionParser默认实现；
        ExpressionParser parser = new SpelExpressionParser();
        // 2）解析表达式：使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。
        Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");
        // 3）构造上下文：准备比如变量定义等等表达式需要的上下文数据。
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("end", "!");
        // 4）求值：通过Expression接口的getValue方法根据上下文获得表达式值。
        System.out.println(expression.getValue(context));
    }

    /**
     * 类相关表达式: 变量定义及引用
     */
    @Test
    public void test03() {
        String expression = "#user.username";
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        User user = new User();
        user.setAddress("长沙");
        user.setUsername("zysheep");
        user.setAge(24);;

        ctx.setVariable("user", user);

        String value = exp.getValue(ctx, String.class);
        System.out.println("value = " + value);
    }

    @Test
    public void test04() {
        String expression = "username";
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        User user = new User();
        user.setAddress("长沙");
        user.setUsername("zysheep");
        user.setAge(24);

        // user 对象设置为 rootObject，那么表达式中就不需要 #user.
        ctx.setRootObject(user);

        String value = exp.getValue(ctx, String.class);
        System.out.println("value = " + value);
    }

    /**
     * 类相关表达式: 对象方法调用
     */
    @Test
    public void test05() {
        String expression = "sayHello(99)";
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);

        StandardEvaluationContext ctx = new StandardEvaluationContext();

        User user = new User();
        user.setAddress("长沙");
        user.setUsername("zysheep");
        user.setAge(24);
        ctx.setRootObject(user);

        String value = exp.getValue(ctx, String.class);
        System.out.println("value = " + value);
    }

    /**
     * 类相关表达式: 对象方法调用
     *
     * 调用无参的 sayHello
     */
    @Test
    public void test06() {
        String expression = "sayHello()";
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);

        StandardEvaluationContext ctx = new StandardEvaluationContext();

        User user = new User();
        user.setAddress("长沙");
        user.setUsername("zysheep");
        user.setAge(24);
        ctx.setRootObject(user);

        String value = exp.getValue(ctx, String.class);
        System.out.println("value = " + value);
    }


    /**
     * 类相关表达式: Bean引用
     */
    @Test
    public void test07() {
        // 通过 SpEL 表达式来调用这个名为 ss 的 bean 中的 sayHello 方法
        String expression = "@ss.sayHello('spel bean引用')";
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        // 给配置的上下文环境设置一个 bean 解析器，这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法
        ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));
        String value = exp.getValue(ctx, String.class);
        System.out.println("value = " + value);
    }
}




